﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

[CustomEditor(typeof(SmoothPathCurve))]
public class SmoothPathCurveEditor : Editor
{
    protected SmoothPathCurve Target { get { return target as SmoothPathCurve; } }
    private SerializedProperty Resolution;
    private SerializedProperty wayPoints;
    private SerializedProperty Looped;
    private SerializedProperty m_Appearance;

    private ReorderableList mWaypointList;
    private bool isFoldout;
    private static bool isShowNormal;
    protected virtual void OnEnable()
    {
        if (this.target == null) return;
        wayPoints = serializedObject.FindProperty("m_Waypoints");
        Resolution = serializedObject.FindProperty("m_Resolution");
        Looped = serializedObject.FindProperty("m_Looped");
        m_Appearance = serializedObject.FindProperty("m_Appearance");

        this.mWaypointList = new ReorderableList(serializedObject, this.wayPoints);
        this.mWaypointList.drawHeaderCallback = rect=> { EditorGUI.LabelField(rect, "Waypoints"); };
        this.mWaypointList.elementHeight = EditorGUIUtility.singleLineHeight;
        this.mWaypointList.drawElementCallback = OnDrawElement;
        this.mWaypointList.onAddCallback = OnAddElement;
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(Resolution, new GUILayoutOption[0]);
        EditorGUILayout.PropertyField(Looped, new GUILayoutOption[0]);
        isShowNormal = EditorGUILayout.Toggle("显示法线", isShowNormal);

        EditorGUILayout.Space(2f);
        EditorGUILayout.PropertyField(m_Appearance, new GUILayoutOption[0]);
        EditorGUILayout.LabelField("Path Length", Target.PathLength.ToString());

        EditorGUILayout.Space(10f);
        mWaypointList.DoLayoutList();

        serializedObject.ApplyModifiedProperties();
    }

    protected virtual void OnSceneGUI()
    {
        if(Tools.current == Tool.Move)
        {
            Color colorOld = Handles.color;
            var localToWorld = Target.transform.localToWorldMatrix;
            for(int i = 0; i < Target.m_Waypoints.Length; ++i)
            {
                DrawSelectHandle(i, localToWorld);
                if (mWaypointList.index == i)
                    DrawPositionControl(i, localToWorld, Target.transform.rotation); // Waypoint is selected
            }
            Handles.color = colorOld;
        }
    }

    [DrawGizmo(GizmoType.Active | GizmoType.NotInSelectionHierarchy
     | GizmoType.InSelectionHierarchy | GizmoType.Pickable, typeof(SmoothPathCurve))]
    static void DrawGizmos(SmoothPathCurve path, GizmoType selectionType)
    {
        var isActive = Selection.activeGameObject == path.gameObject;
        DrawPathGizmo(path, isActive ? path.m_Appearance.pathColor : path.m_Appearance.inactivePathColor, isActive);
    }

    protected void OnDrawElement(Rect rect, int index, bool isActive, bool isForce)
    {
        SerializedProperty arrayElementAtIndex = this.wayPoints.GetArrayElementAtIndex(index);

        float hSpace = 3;
        rect.width -= hSpace;
        rect.y += 1;
        Vector2 numberDimension = GUI.skin.label.CalcSize(new GUIContent("999"));
        Rect r = new Rect(rect.position, numberDimension);
        if(GUI.Button(r,new GUIContent(index.ToString(), "跳转到Scene上的路径点")))
        {
            if(SceneView.lastActiveSceneView != null)
            {
                mWaypointList.index = index;
                SceneView.lastActiveSceneView.pivot = Target.EvaluatePosition(index);
                SceneView.lastActiveSceneView.size = 4;
                SceneView.lastActiveSceneView.Repaint();
            }
        }

        float floatFieldWidth = EditorGUIUtility.singleLineHeight * 2f;
        GUIContent rollLabel = new GUIContent("Roll");
        Vector2 labelDimension = GUI.skin.label.CalcSize(rollLabel);
        float rollWidth = labelDimension.x + floatFieldWidth;
        r.x += r.width + hSpace; r.width = rect.width - (r.width + hSpace + rollWidth) - (r.height + hSpace);
        EditorGUI.PropertyField(r, arrayElementAtIndex.FindPropertyRelative("position"), GUIContent.none);

        r.x += r.width + hSpace; r.width = rollWidth;
        float oldWidth = EditorGUIUtility.labelWidth;
        EditorGUIUtility.labelWidth = labelDimension.x;

        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;
        EditorGUI.PropertyField(r, arrayElementAtIndex.FindPropertyRelative("roll"), rollLabel);
        EditorGUIUtility.labelWidth = oldWidth;
        EditorGUI.indentLevel = indent;


        r.x += r.width + hSpace; r.height += 1; r.width = r.height;
        GUIContent setButtonContent = EditorGUIUtility.IconContent("d_RectTransform Icon");
        setButtonContent.tooltip = "Set to scene-view camera position";
        if (GUI.Button(r, setButtonContent, GUI.skin.label) && SceneView.lastActiveSceneView != null)
        {
            Undo.RecordObject(Target, "Set waypoint");
            SmoothPathCurve.Waypoint wp = Target.m_Waypoints[index];
            Vector3 pos = SceneView.lastActiveSceneView.camera.transform.position;
            wp.position = Target.transform.InverseTransformPoint(pos);
            Target.m_Waypoints[index] = wp;
        }
    }

    protected void OnAddElement(ReorderableList l)
    {
        Vector3 pos = Vector3.right;
        float roll = 0;
        int numWaypoints = Target.m_Waypoints.Length;
        int indexA = l.index;
        if (indexA < 0) indexA = numWaypoints - 1;
        if (indexA >= 0)
        {
            int indexB = indexA + 1;
            if (Target.m_Looped && indexB >= numWaypoints)
                indexB = 0;
            if (indexB >= numWaypoints)
            {
                Vector3 delta = Vector3.right;
                if (indexA > 0)
                    delta = Target.m_Waypoints[indexA].position - Target.m_Waypoints[indexA - 1].position;
                pos = Target.m_Waypoints[indexA].position + delta;
                roll = Target.m_Waypoints[indexA].roll;
            }
            else
            {
                // Interpolate
                pos = Target.transform.InverseTransformPoint(Target.EvaluatePosition(0.5f + indexA));
                roll = Mathf.Lerp(Target.m_Waypoints[indexA].roll, Target.m_Waypoints[indexB].roll, 0.5f);
            }
        }

        Undo.RecordObject(Target, "Add waypoint");
        var wp = new SmoothPathCurve.Waypoint();
        wp.position = pos;
        wp.roll = roll;
        var list = new List<SmoothPathCurve.Waypoint>(Target.m_Waypoints);
        list.Insert(indexA + 1, wp);
        Target.m_Waypoints = list.ToArray();
        Target.InvalidateDistanceCache();
        EditorApplication.QueuePlayerLoopUpdate();
        InternalEditorUtility.RepaintAllViews();
        mWaypointList.index = indexA + 1; // select it

    }


    void DrawSelectHandle(int i,Matrix4x4 localToWorld)
    {
        if(Event.current.button != 1)
        {
            Vector3 pos = localToWorld.MultiplyPoint(Target.m_Waypoints[i].position);
            float size = HandleUtility.GetHandleSize(pos) * 0.2f;
            Handles.color = Color.white;
            if(Handles.Button(pos,Quaternion.identity,size,size,Handles.SphereHandleCap) && mWaypointList.index != i)
            {
                mWaypointList.index = i;
                EditorApplication.QueuePlayerLoopUpdate();
                InternalEditorUtility.RepaintAllViews();
            }

            Handles.BeginGUI();
            Vector2 labelSize = new Vector2(
            EditorGUIUtility.singleLineHeight * 2, EditorGUIUtility.singleLineHeight);
            Vector2 labelPos = HandleUtility.WorldToGUIPoint(pos);
            labelPos.y -= labelSize.y / 2;
            labelPos.x -= labelSize.x / 2;
            GUILayout.BeginArea(new Rect(labelPos, labelSize));
            GUIStyle style = new GUIStyle();
            style.normal.textColor = Color.black;
            style.alignment = TextAnchor.MiddleCenter;
            GUILayout.Label(new GUIContent(i.ToString(), "Waypoint " + i), style);
            GUILayout.EndArea();
            Handles.EndGUI();
        }
    }


    void DrawPositionControl(int i, Matrix4x4 localToWorld, Quaternion localRotation)
    {
        SmoothPathCurve.Waypoint wp = Target.m_Waypoints[i];
        Vector3 pos = localToWorld.MultiplyPoint(wp.position);
        EditorGUI.BeginChangeCheck();
        Handles.color = Target.m_Appearance.pathColor;
        Quaternion rotation = (Tools.pivotRotation == PivotRotation.Local)
            ? localRotation : Quaternion.identity;
        pos = Handles.PositionHandle(pos, rotation);
        if (EditorGUI.EndChangeCheck())
        {
            Undo.RecordObject(target, "Move Waypoint");
            wp.position = Matrix4x4.Inverse(localToWorld).MultiplyPoint(pos);
            Target.UpdatePoint(i,wp.position);
            Target.InvalidateDistanceCache();
            EditorUtility.SetDirty(Target);
            EditorApplication.QueuePlayerLoopUpdate();
            InternalEditorUtility.RepaintAllViews();
        }
    }

    static void DrawPathGizmo(SmoothPathCurve path, Color pathColor, bool isActive)
    {
        // Draw the path
        Color colorOld = Handles.color;
        Handles.color = pathColor;
        float step = 1f / path.m_Resolution;
        float halfWidth = path.m_Appearance.width * 0.5f;
        Vector3 lastPos = path.EvaluatePosition(path.MinPos);
        Vector3 lastW = (path.EvaluateOrientation(path.MinPos)
                         * Vector3.right) * halfWidth;
        float tEnd = path.MaxPos + step / 2;
        for (float t = path.MinPos ; t <= tEnd; t += step)
        {
            Vector3 p = path.EvaluatePosition(t);
            if (!isActive || halfWidth == 0)
            {
                Handles.DrawLine(p, lastPos);
            }
            else
            {
                Quaternion q = path.EvaluateOrientation(t);
                Vector3 w = (q * Vector3.right) * halfWidth;
                Vector3 w2 = w * 1.0f;
                Vector3 p0 = p - w2;
                Vector3 p1 = p + w2;
                Handles.DrawLine(p0, p1);
                Handles.DrawLine(lastPos - lastW, p - w);
                Handles.DrawLine(lastPos + lastW, p + w);
                if (isShowNormal)
                {
                    // Show the normals, for debugging
                    Handles.color = Color.red;
                    Vector3 y = (q * Vector3.up) * halfWidth;
                    Handles.DrawLine(p, p + y);
                    Handles.color = pathColor;
                }


                lastW = w;
            }

            lastPos = p;
        }
        Handles.color = colorOld;
    }
}
